package com.zhijian.medical.service.impl;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.zhijian.medical.dao.local.NgsMapper;
import com.zhijian.medical.entity.dto.NgsSjfxDto;
import com.zhijian.medical.entity.dto.NgsSjpzqSjDto;
import com.zhijian.medical.entity.dto.SangerSjpzqPcrDto;
import com.zhijian.medical.entity.po.*;
import com.zhijian.medical.entity.vo.request.ExperimentProcessSampleSearchVo;
import com.zhijian.medical.entity.vo.request.NgsSaveOrUpdateVo;
import com.zhijian.medical.entity.vo.request.NgsSearchVo;
import com.zhijian.medical.entity.vo.response.ExperimentProcessSampleHstqVo;
import com.zhijian.medical.entity.vo.response.NgsVo;
import com.zhijian.medical.enums.ExperimentTypeEnum;
import com.zhijian.medical.enums.NgsSampleStatusEnum;
import com.zhijian.medical.enums.NgsStatusEnum;
import com.zhijian.medical.holder.CurrentUserHolder;
import com.zhijian.medical.service.*;
import com.zhijian.medical.util.AssertUtil;
import com.zhijian.medical.util.JsonUtil;
import com.zhijian.medical.util.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 二代测序主表业务处理层
 *
 * @author HCG
 * @version: 1.0
 * @date 2022-08-23 14:59:59
 */
@Slf4j
@Service
public class NgsServiceImpl implements NgsService {

    @Autowired
    private NgsMapper mapper;

    @Autowired
    private ExperimentProcessSampleService experimentProcessSampleService;

    @Autowired
    private NgsSjpzqService ngsSjpzqService;

    @Autowired
    private NgsHszbqService ngsHszbqService;

    @Autowired
    private NgsWkzbq1Service ngsWkzbq1Service;

    @Autowired
    private NgsWkzbq2Service ngsWkzbq2Service;

    @Autowired
    private NgsWkzbq3Service ngsWkzbq3Service;

    @Autowired
    private NgsWkfjq1Service ngsWkfjq1Service;

    @Autowired
    private NgsWkfjq2Service ngsWkfjq2Service;

    @Autowired
    private NgsWkfjq3Service ngsWkfjq3Service;

    @Autowired
    private NgsWkfjq4Service ngsWkfjq4Service;

    @Autowired
    private NgsCxqService ngsCxqService;

    @Autowired
    private NgsSjfxService ngsSjfxService;

    @Autowired
    private ExperimentProcessSampleLogService experimentProcessSampleLogService;

    @Autowired
    private ExperimentProcessSampleHstqService experimentProcessSampleHstqService;

    /**
     * 编号
     */
    private static final String number = "FZ-SOP-084-01-%s-%s";

    @Override
    public Ngs selectById(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public NgsVo selectVoById(Long id) {
        NgsVo ngsVo = mapper.selectVoById(id);
        if (Objects.isNull(ngsVo)) {
            return null;
        }
        ngsVo.setExperimentProcessSampleList(experimentProcessSampleService.selectList(ExperimentProcessSampleSearchVo.builder()
                .experimentProcessId(id)
                .mainType(ExperimentTypeEnum.NGS.getCode().byteValue())
                .build()));
        return ngsVo;
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public void deleteById(Long id) {
        mapper.deleteByPrimaryKey(id);
        ngsSjpzqService.deleteByNgsId(id);
        ngsHszbqService.deleteByNgsId(id);
        ngsWkzbq1Service.deleteByNgsId(id);
        ngsWkzbq2Service.deleteByNgsId(id);
        ngsWkzbq3Service.deleteByNgsId(id);
        ngsWkfjq1Service.deleteByNgsId(id);
        ngsWkfjq2Service.deleteByNgsId(id);
        ngsWkfjq3Service.deleteByNgsId(id);
        ngsWkfjq4Service.deleteByNgsId(id);
        ngsCxqService.deleteByNgsId(id);
        ngsSjfxService.deleteByNgsId(id);
    }

    @Override
    public Long insert(Ngs ngs) {
        Date now = new Date();
        ngs.setCreateTime(now);
        ngs.setUpdateTime(now);
        return mapper.insert(ngs) > 0 ? ngs.getId() : 0L;
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public Long insert(NgsSaveOrUpdateVo ngsSaveOrUpdateVo) {
        Ngs ngs = new Ngs();
        BeanUtils.copyProperties(ngsSaveOrUpdateVo, ngs);
        ngs.setStatus(NgsStatusEnum.SJPZQ.getCode().byteValue());
        Date now = new Date();
        String today = DateUtil.formatDate(now);
        ngs.setStartTime(now);
        //今天有几个实验了
        Integer count = this.count(NgsSearchVo.builder()
                .startDay(today)
                .build());
        String processNumber = String.format(number, DateUtil.format(now, "yyyyMMdd"), String.format("%02d", count + 1));
        ngs.setProcessNumber(processNumber);
        //先保存主表
        Long ngsId = this.insert(ngs);
        //再保存其他数据
        saveOtherData(ngsId, ngsSaveOrUpdateVo.getName(), processNumber, ngsSaveOrUpdateVo.getSampleIds());
        return ngsId;
    }

    private void saveOtherData(Long ngsId, String ngsName, String processNumber, List<Long> sampleIds) {

        final int[] number = {1};

        List<SangerSjpzqPcrDto> pdhfyybDataList = Lists.newLinkedList();

        //数据分析数据
        List<NgsSjfxDto> sjfxDataList = Lists.newLinkedList();

        //样本日志数据
        List<ExperimentProcessSampleLog> experimentProcessSampleLogList = Lists.newArrayList();

        //核酸提取记录id
        final Long[] hstqId = {null};
        sampleIds.forEach(s -> {
            ExperimentProcessSample experimentProcessSample = experimentProcessSampleService.selectById(s);
            AssertUtil.notNull(experimentProcessSample, "有样本不存在或者已被删除");
            hstqId[0] = experimentProcessSample.getHtstqId();

            //样本的监测项目
            String[] samplerProjects = experimentProcessSample.getProject().split(",");
            Arrays.stream(samplerProjects).forEach(s1 -> {
                SangerSjpzqPcrDto sangerSjpzqPcrFDto = new SangerSjpzqPcrDto();
                sangerSjpzqPcrFDto.setNumber(number[0]);
                sangerSjpzqPcrFDto.setFzblh(experimentProcessSample.getMoleculePathologyNumber());
                sangerSjpzqPcrFDto.setProject(s1);
                pdhfyybDataList.add(sangerSjpzqPcrFDto);
                number[0]++;
                sjfxDataList.add(NgsSjfxDto.builder()
                        .fzblh(experimentProcessSample.getMoleculePathologyNumber())
                        .cgblh(experimentProcessSample.getConventionPathologyNumber())
                        .project(s1)
                        .build());
            });

            experimentProcessSampleService.updateByPrimaryKeySelective(ExperimentProcessSample.builder()
                    .id(s)
                    .experimentProcessId(ngsId)
                    //状态改为检测中
                    .status(NgsSampleStatusEnum.JCZ.getCode().byteValue())
                    .build());

            experimentProcessSampleLogList.add(ExperimentProcessSampleLog.builder()
                    .sampleId(s)
                    .fishId(ngsId)
                    .mainType(experimentProcessSample.getMainType())
                    .op(String.format("%s%s-待检测", ngsName, processNumber))
                    .opUser(CurrentUserHolder.getUser().getName())
                    .build());

        });

        //保存样本日志
        experimentProcessSampleLogService.saveBatch(experimentProcessSampleLogList);

        //试剂配制区
        NgsSjpzq ngsSjpzq = new NgsSjpzq();
        ngsSjpzq.setNgsId(ngsId);


        List<NgsSjpzqSjDto> sjData1List = Lists.newArrayList();
        List<NgsSjpzqSjDto> sjData2List = Lists.newArrayList();
        List<NgsSjpzqSjDto> sjData3List = Lists.newArrayList();
        List<NgsSjpzqSjDto> sjData4List = Lists.newArrayList();

        sjData1List.add(NgsSjpzqSjDto.builder()
                .name("Frag Buffer（10X）")
                .tj(5)
                .sl(sampleIds.size())
                .ztj(5 * sampleIds.size())
                .build());

        sjData1List.add(NgsSjpzqSjDto.builder()
                .name("Frag Enzyme")
                .tj(10)
                .sl(sampleIds.size())
                .ztj(10 * sampleIds.size())
                .build());
        ngsSjpzq.setSjData1(JsonUtil.toString(sjData1List));

        sjData2List.add(NgsSjpzqSjDto.builder()
                .name("End Repair & A-Tailing Buffer")
                .tj(7)
                .sl(sampleIds.size())
                .ztj(7 * sampleIds.size())
                .build());

        sjData2List.add(NgsSjpzqSjDto.builder()
                .name("End Repair & A-Tailing Enzyme Mix")
                .tj(3)
                .sl(sampleIds.size())
                .ztj(3 * sampleIds.size())
                .build());

        ngsSjpzq.setSjData2(JsonUtil.toString(sjData2List));

        sjData3List.add(NgsSjpzqSjDto.builder()
                .name("NF water")
                .tj(5)
                .sl(sampleIds.size())
                .ztj(5 * sampleIds.size())
                .build());

        sjData3List.add(NgsSjpzqSjDto.builder()
                .name("Ligation Buffer")
                .tj(30)
                .sl(sampleIds.size())
                .ztj(30 * sampleIds.size())
                .build());

        sjData3List.add(NgsSjpzqSjDto.builder()
                .name("DNA Ligase")
                .tj(10)
                .sl(sampleIds.size())
                .ztj(10 * sampleIds.size())
                .build());

        ngsSjpzq.setSjData3(JsonUtil.toString(sjData3List));

        sjData4List.add(NgsSjpzqSjDto.builder()
                .name("KAPA HiFi HotStart ReadyMix (2X)")
                .tj(25)
                .sl(sampleIds.size())
                .ztj(25 * sampleIds.size())
                .build());

        sjData4List.add(NgsSjpzqSjDto.builder()
                .name("illumina Primer Mix")
                .tj(5)
                .sl(sampleIds.size())
                .ztj(5 * sampleIds.size())
                .build());

        ngsSjpzq.setSjData4(JsonUtil.toString(sjData4List));

        //核酸制备区
        NgsHszbq ngsHszbq = new NgsHszbq();
        ngsHszbq.setNgsId(ngsId);
        ngsHszbq.setPdhfyybData(JsonUtil.toString(pdhfyybDataList));

        //如果之前做过核酸提取，把一些数据同步过来
        if (Objects.nonNull(hstqId[0])) {
            ExperimentProcessSampleHstqVo experimentProcessSampleHstqVo = experimentProcessSampleHstqService.selectVoById(hstqId[0]);
            if (Objects.nonNull(experimentProcessSampleHstqVo)) {
                ngsHszbq.setLcbgwd(experimentProcessSampleHstqVo.getSyqlcbxwd());
                ngsHszbq.setSystmqj(experimentProcessSampleHstqVo.getSyqsystmqj());
                ngsHszbq.setGcywyc(experimentProcessSampleHstqVo.getHstqgcywyc());
                ngsHszbq.setClfs(experimentProcessSampleHstqVo.getHstqgcclfs());
                ngsHszbq.setZdq(experimentProcessSampleHstqVo.getZdq());
                ngsHszbq.setAqgt(experimentProcessSampleHstqVo.getSwaqg());
                ngsHszbq.setQjjzwxzssygztm(experimentProcessSampleHstqVo.getSyhqjjzwzssygztm());
                ngsHszbq.setQjyyq(experimentProcessSampleHstqVo.getSyhqjyyq());
                ngsHszbq.setQjlxj(experimentProcessSampleHstqVo.getSyhqjlxj());
                ngsHszbq.setClsyfqw(experimentProcessSampleHstqVo.getSyhclsyfqw());
            }
        }


        //文库制备区（片段化）
        NgsWkzbq1 ngsWkzbq1 = new NgsWkzbq1();
        ngsWkzbq1.setNgsId(ngsId);

        //文库制备区（末端修复&加A）
        NgsWkzbq2 ngsWkzbq2 = new NgsWkzbq2();
        ngsWkzbq2.setNgsId(ngsId);
        ngsWkzbq2.setChcwfygData(JsonUtil.toString(pdhfyybDataList));

        //文库制备区（接头连接）
        NgsWkzbq3 ngsWkzbq3 = new NgsWkzbq3();
        ngsWkzbq3.setNgsId(ngsId);
        ngsWkzbq3.setChcwfygData(JsonUtil.toString(pdhfyybDataList));

        //文库富集区（文库富集）
        NgsWkfjq1 ngsWkfjq1 = new NgsWkfjq1();
        ngsWkfjq1.setNgsId(ngsId);
        ngsWkfjq1.setChcwfygbhData(JsonUtil.toString(pdhfyybDataList));

        //文库富集区（DNA文库混合及杂交）
        NgsWkfjq2 ngsWkfjq2 = new NgsWkfjq2();
        ngsWkfjq2.setNgsId(ngsId);

        //文库富集区（文库捕获）
        NgsWkfjq3 ngsWkfjq3 = new NgsWkfjq3();
        ngsWkfjq3.setNgsId(ngsId);
        ngsWkfjq3.setQxcwbhData(JsonUtil.toString(pdhfyybDataList));

        //文库富集区（捕获文库富集及pooling）
        NgsWkfjq4 ngsWkfjq4 = new NgsWkfjq4();
        ngsWkfjq4.setNgsId(ngsId);
        ngsWkfjq4.setChhwkbhData(JsonUtil.toString(pdhfyybDataList));

        //测序区（上机测序）
        NgsCxq ngsCxq = new NgsCxq();
        ngsCxq.setNgsId(ngsId);

        //数据分析（测序室、数据室）
        NgsSjfx ngsSjfx = new NgsSjfx();
        ngsSjfx.setNgsId(ngsId);
        ngsSjfx.setData(JsonUtil.toString(sjfxDataList));


        ngsSjpzqService.insert(ngsSjpzq);
        ngsHszbqService.insert(ngsHszbq);
        ngsWkzbq1Service.insert(ngsWkzbq1);
        ngsWkzbq2Service.insert(ngsWkzbq2);
        ngsWkzbq3Service.insert(ngsWkzbq3);
        ngsWkfjq1Service.insert(ngsWkfjq1);
        ngsWkfjq2Service.insert(ngsWkfjq2);
        ngsWkfjq3Service.insert(ngsWkfjq3);
        ngsWkfjq4Service.insert(ngsWkfjq4);
        ngsCxqService.insert(ngsCxq);
        ngsSjfxService.insert(ngsSjfx);

    }

    @Override
    public boolean update(Ngs ngs) {
        Date now = new Date();
        ngs.setUpdateTime(now);
        return mapper.updateByPrimaryKey(ngs) != 0;
    }

    @Override
    public boolean update(NgsSaveOrUpdateVo ngsSaveOrUpdateVo) {
        Ngs oldNgs = this.selectById(ngsSaveOrUpdateVo.getId());
        AssertUtil.notNull(oldNgs, "此实验不存在或者已被删除");
        Ngs ngs = new Ngs();
        BeanUtils.copyProperties(ngsSaveOrUpdateVo, ngs);
        return this.updateByPrimaryKeySelective(ngs);
    }

    @Override
    public boolean updateByPrimaryKeySelective(Ngs record) {
        return mapper.updateByPrimaryKeySelective(record) != 0;
    }

    @Override
    public Integer count(NgsSearchVo ngsSearchVo) {
        return mapper.count(ngsSearchVo);
    }

    @Override
    public List<NgsVo> selectList(NgsSearchVo ngsSearchVo) {
        return mapper.selectList(ngsSearchVo);
    }

    @Override
    public Page<NgsVo> selectPage(NgsSearchVo ngsSearchVo) {
        //先直接查总数
        int count = count(ngsSearchVo);
        //分页
        Page<NgsVo> page = new Page<>(true, count, ngsSearchVo.getPageNum(), ngsSearchVo.getPageSize());
        ngsSearchVo.setStartIndex(page.getStartIndex());
        page.setPage(true);
        page.setTotalCount(count);
        List<NgsVo> responseVos = this.selectList(ngsSearchVo);
        page.setData(responseVos);
        return page;
    }

}